package com.gss.stockLedger.service;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

import org.springframework.beans.BeanUtils;

import com.gss.stockLedger.dao.QueryStockNoDaoSupportDAO;
import com.gss.stockLedger.domain.FIFOReportDTO;
import com.gss.stockLedger.domain.StockTransactionDtls;
import com.gss.stockLedger.form.ReportGenerateForm;

public class WacDataProcessorThread implements Runnable {

	private final GenerateExcelThread generateExcelThread;
	private final Object[] partIdDetails;
	private final QueryStockNoDaoSupportDAO queryStockNoDaoSupportDAO;
	private final ReportGenerateForm reportForm;
	private final ThreadCounter threadCounter;
	
	public WacDataProcessorThread(GenerateExcelThread excelThread,Object[] partIdDetails,QueryStockNoDaoSupportDAO dao
			,ReportGenerateForm reportGenerateForm, ThreadCounter threadCounter){
		this.generateExcelThread = excelThread;
		this.partIdDetails = partIdDetails;
		this.queryStockNoDaoSupportDAO = dao;
		this.reportForm = reportGenerateForm;
		this.threadCounter = threadCounter;
	}
	
	@Override
	public void run() {
		
		List<FIFOReportDTO> finalReportList = new ArrayList<FIFOReportDTO>();
		List<StockTransactionDtls> stockTransactionDtls = null;
		
		finalReportList = new ArrayList<FIFOReportDTO>();
	
		try {
			stockTransactionDtls= queryStockNoDaoSupportDAO.getStockTransactionsForProduct(reportForm,partIdDetails[0].toString(),partIdDetails[1].toString());
		} catch (Exception e) {
			e.printStackTrace();
			this.threadCounter.setNumberOfThreads(-1);
			return;
		}
//		System.out.println("number of stock entries to process: " + stockTransactionDtls.size());
		if(stockTransactionDtls.size() == 0){
			this.threadCounter.setNumberOfThreads(-1);
			return;
		}
		
		//sorted list according to receipt date
		
		LinkedList<FIFOReportDTO> transactionQ = null;
		Stack<FIFOReportDTO> consumedStack = null;
		LinkedList<FIFOReportDTO> transactionQForReport = null;
		Date previousTransactDate = null;
		Calendar lastTransactionDate = Calendar.getInstance();
		Calendar currentTransactionDate = Calendar.getInstance();
		long prevClosingQuantity = 0;
		double consumptionRate = 0.0F;
		
		transactionQ = new LinkedList<FIFOReportDTO>();
		transactionQForReport = new LinkedList<FIFOReportDTO>();
		consumedStack = new Stack<FIFOReportDTO>();
		
		long activeQuantity=0;
		float activeRate=0;
		double activeValue=0.0;
		float currentRate = 0.0F;
		FIFOReportDTO activeRow ;
	
		
	
		// in the following loop, all receipts are accumulated into a linked list so that they can be later used for consumption following FIFO
		try{
			for (StockTransactionDtls stockTransactionDtls2 : stockTransactionDtls) {
				activeRow = new FIFOReportDTO();
				BeanUtils.copyProperties(stockTransactionDtls2, activeRow);
				
				if(activeRow.getTransactionType().equalsIgnoreCase("ZGRIR")) {
					//change code to incorporate multiple grir entries on same day with same assignment number
					if(transactionQ.size()>0&&transactionQ.contains(activeRow))
					{
						int index =transactionQ.indexOf(activeRow);
						FIFOReportDTO fromQueue = transactionQ.get(index);
						fromQueue.setQuantity(fromQueue.getQuantity()+activeRow.getQuantity());
						fromQueue.setValue(fromQueue.getValue() + activeRow.getQuantity()*activeRow.getRate());
	// commented by Mayank
	//							if(fromQueue.getQuantity()<=0)
	//								transactionQ.remove(index);
					}
					else
					{
						FIFOReportDTO temp = new FIFOReportDTO();
						BeanUtils.copyProperties(activeRow, temp);
						temp.setValue((double) (temp.getQuantity() * temp.getRate()));
						transactionQ.add(temp);
					}
				}
			}
		
			// applying all adjustments on respective Purchases (GRIR)
		
			for (StockTransactionDtls stockTransactionDtls2 : stockTransactionDtls) {
				activeRow = new FIFOReportDTO();
				BeanUtils.copyProperties(stockTransactionDtls2, activeRow);
				
				if(activeRow.getTransactionType().equalsIgnoreCase("CUSTOM") || activeRow.getTransactionType().equalsIgnoreCase("COSTPRICE") || 
						activeRow.getTransactionType().equalsIgnoreCase("RETRO") || activeRow.getTransactionType().equalsIgnoreCase("FOREX")
						||activeRow.getTransactionType().equalsIgnoreCase("INVADJ")) {
					
					for (FIFOReportDTO fifoReportDTO : transactionQ) {
						if(null != activeRow.getAssignmentId() && activeRow.getAssignmentId().equalsIgnoreCase(fifoReportDTO.getAssignmentId())){
							if(activeRow.getTransactionType().equalsIgnoreCase("INVADJ")){
								if(activeRow.getQuantity()!=null)
								fifoReportDTO.setQuantity(fifoReportDTO.getQuantity()+activeRow.getQuantity());
							}
							if(activeRow.getValue()!=null)
								fifoReportDTO.setValue(fifoReportDTO.getValue() + activeRow.getValue());
							
							if(fifoReportDTO.getQuantity() != 0)
								fifoReportDTO.setRate((float) (fifoReportDTO.getValue() / fifoReportDTO.getQuantity()));
							else
								fifoReportDTO.setRate(0.0f);
							
							break;
						}
					}
				}
			}
		
			// above loop results into a transactionQ with all receipts along with their reversals and adjustments
			// running loop on this transactionQ, to remove any entries with zero or negative quantity or value
			List<FIFOReportDTO> toRemoveList = new ArrayList<FIFOReportDTO>();
			for(FIFOReportDTO receipts : transactionQ){
				if(receipts.getQuantity() <=0 || receipts.getValue() <=0){
					//transactionQ.remove(transactionQ.indexOf(receipts));
					toRemoveList.add(receipts);
				}else {
					receipts.setRate((float) (receipts.getValue() / receipts.getQuantity()));
				}
			}
			if(toRemoveList.size() > 0){
				for(FIFOReportDTO index : toRemoveList)
					transactionQ.remove(index);
			}
		
			// now we have transactionQ with all entries having positive Quantity and positive value and rate populated based on final values
		
			long counter=-1;
			FIFOReportDTO previousRow = null;
			for (StockTransactionDtls stockTransactionDtls2 : stockTransactionDtls) {
			
				activeRow = new FIFOReportDTO();
				BeanUtils.copyProperties(stockTransactionDtls2, activeRow);
				counter++;
				if(counter==0)
				{
					if(activeRow.getTransactionDate().after(reportForm.getSecStartDate())){
						finalReportList.add(activeRow);
					}
					
					if(activeRow.getTransactionType().equalsIgnoreCase("ZGRIR"))
					{
						FIFOReportDTO temp = new FIFOReportDTO();
						BeanUtils.copyProperties(activeRow, temp);
						if(transactionQ.size()>0 && transactionQ.contains(temp))
						{
							temp.setRate(transactionQ.getFirst().getRate());
							temp.setValue(transactionQ.getFirst().getValue());
							transactionQForReport.add(temp);
							transactionQ.removeFirst();
						}
						
						 consumptionRate = temp.getRate();
					}
					else if(activeRow.getTransactionType().equalsIgnoreCase("OPENING")){
						FIFOReportDTO temp = new FIFOReportDTO();
						BeanUtils.copyProperties(activeRow, temp);
						//temp.setRate((float) (temp.getValue() / temp.getQuantity()));
						temp.setValue((double) (temp.getRate()*temp.getQuantity()));
						temp.setAssignmentId("OpeningGRN");
						transactionQForReport.add(temp);
						
						//change to show value as product of quantity and rate
						activeRow.setValue((double) (temp.getRate()*temp.getQuantity()));
						
						consumptionRate = temp.getRate();
						//transactionQ.removeFirst();
					}else if(WACReportGenerationThreadedService.Consumption_Transaction_Type.contains(activeRow.getTransactionType())){
						
						FIFOReportDTO consumedStockDTO = new FIFOReportDTO();
						consumedStockDTO.setTransactionType("ZGRIR");
						consumedStockDTO.setAssignmentId("ApplicationGenerated");
						consumedStockDTO.setQuantity(activeRow.getQuantity());
						consumedStockDTO.setValue(0.0d);
						consumedStockDTO.setRate(currentRate);
						consumedStack.push(consumedStockDTO);
						
						activeRow.setQuantity(activeRow.getQuantity() * -1);
						activeRow.setRate(0.0f);
						activeRow.setValue(0.0d);
						consumptionRate = 0.0f;
					}
					
					activeRow.setClosingQuantity(activeRow.getQuantity()!=null?activeRow.getQuantity():0);
					activeRow.setClosingRate(activeRow.getRate()!=null?activeRow.getRate():0);
					activeRow.setClosingValue(activeRow.getValue()!=null?activeRow.getValue():0);
					activeQuantity = activeRow.getQuantity()!=null?activeRow.getQuantity():0;
					if(activeRow.getRate()!=null&&activeRow.getQuantity()!=null)
						activeValue = activeRow.getRate()*activeRow.getQuantity();
					else
						activeValue = activeRow.getValue();
	
					continue;
				}
				
				String transactionType = stockTransactionDtls2.getTransactionType();
				
				if(transactionType.equalsIgnoreCase("ZGRIR")) //grir is purchase 
				{
					FIFOReportDTO temp = new FIFOReportDTO();
					BeanUtils.copyProperties(activeRow, temp);
					if(transactionQ.size()>0 && transactionQ.contains(temp))
					{
						temp.setRate(transactionQ.getFirst().getRate());
						temp.setValue(transactionQ.getFirst().getValue());
						transactionQForReport.add(temp);
						transactionQ.removeFirst();
					}
					
					activeRow.setClosingQuantity(activeQuantity+=activeRow.getQuantity());
					activeRow.setClosingValue(activeRow.getValue()!=null?activeValue+=activeRow.getValue():activeValue);
					activeRow.setClosingRate((float) (activeValue/activeQuantity));
					if(activeRow.getClosingQuantity()!=0)
						consumptionRate = activeRow.getClosingValue()/activeRow.getClosingQuantity();
					else
						consumptionRate = 0;
				}
				else if(transactionType.equalsIgnoreCase("CPA"))
				{
					
				}
				else if(WACReportGenerationThreadedService.Consumption_Transaction_Type.contains(transactionType)){
					
					long consumedQuantity = 0;
					long quantityToConsume = activeRow.getQuantity();
					double consumedQuantityValue = consumptionRate*quantityToConsume;
					
	//					if(quantityToConsume>activeQuantity)
	//					{
	//						System.out.println("Invalid negative entry");
	//					}
	//					else
	//						System.out.println("valid entry");
					/*
					while(quantityToConsume>0)
					{
						
						if(transactionQForReport!=null&&transactionQForReport.size()>0)
						{
							FIFOReportDTO purchaseToConsume = transactionQForReport.getFirst();
						
							if(quantityToConsume>purchaseToConsume.getQuantity())
							{
								quantityToConsume-=purchaseToConsume.getQuantity();
								consumedQuantity+=purchaseToConsume.getQuantity();
								consumedQuantityValue+=(purchaseToConsume.getQuantity()*purchaseToConsume.getRate());
								transactionQForReport.removeFirst();
								currentRate = purchaseToConsume.getRate();
								
								if(consumedStack.peek().equals(purchaseToConsume)){
									consumedStack.peek().setQuantity((long) (purchaseToConsume.getValue() / purchaseToConsume.getRate()));
								}else{
									FIFOReportDTO temp = new FIFOReportDTO();
									BeanUtils.copyProperties(purchaseToConsume, temp);
									consumedStack.push(temp);
								}
								
								
							}
							else
							{
								purchaseToConsume.setQuantity(purchaseToConsume.getQuantity()-quantityToConsume);
								consumedQuantity += quantityToConsume;
								consumedQuantityValue +=quantityToConsume*purchaseToConsume.getRate();
								quantityToConsume =0;
								
								if(!consumedStack.empty()&&consumedStack.peek().equals(purchaseToConsume)){
									consumedStack.peek().setQuantity((long) (purchaseToConsume.getValue() / purchaseToConsume.getRate()) - purchaseToConsume.getQuantity());
								}else{
									FIFOReportDTO temp = new FIFOReportDTO();
									BeanUtils.copyProperties(purchaseToConsume, temp);
									temp.setQuantity((long) (purchaseToConsume.getValue() / purchaseToConsume.getRate()) - purchaseToConsume.getQuantity());
									consumedStack.push(temp);
								}
							}
						}
						else
						{
	//							System.out.println("inventory exhausted");
							FIFOReportDTO consumedStockDTO = new FIFOReportDTO();
							consumedStockDTO.setTransactionType("ZGRIR");
							consumedStockDTO.setAssignmentId("ApplicationGenerated");
							consumedStockDTO.setQuantity(quantityToConsume);
							if(currentRate != 0)
								consumedStockDTO.setValue((double) (quantityToConsume*currentRate));
							else
								consumedStockDTO.setValue(0.0d);
							consumedStockDTO.setRate(currentRate);
							consumedStack.push(consumedStockDTO);
							
							consumedQuantity+=quantityToConsume;
							consumedQuantityValue += quantityToConsume*currentRate;
							quantityToConsume=0;
							
						}
					}*/
	
					activeRow.setClosingQuantity(activeQuantity-=quantityToConsume); 
					activeRow.setClosingValue(activeValue-=consumedQuantityValue);
	
					if(activeQuantity!=0)
						activeRow.setClosingRate((float) (activeValue/activeQuantity));
					else
						activeRow.setClosingRate(0.0f);
					
					activeRow.setRate((float) consumptionRate);
					activeRow.setQuantity(quantityToConsume*-1);
					activeRow.setValue(consumedQuantityValue * -1);
					
					//wacReportList.add(activeRow);
				}
				/*else if(Consumption_Transaction_Type.contains(transactionType) && activeRow.getQuantity() < 0){/*
					long quantityToReverse = activeRow.getQuantity() * -1l;
					long reversedQuantity = 0;
					double reversedQuantityValue = 0.0d;
					while(quantityToReverse > 0){
						FIFOReportDTO consumptionEntry = consumedStack.peek();
						if(consumptionEntry.getQuantity()>quantityToReverse){
							consumptionEntry.setQuantity(consumptionEntry.getQuantity() - quantityToReverse);
							reversedQuantityValue+= quantityToReverse * consumptionEntry.getRate();
							if(!consumptionEntry.getAssignmentId().equalsIgnoreCase("ApplicationGenerated")){
								if(transactionQForReport.getFirst().equals(consumptionEntry)){
									transactionQForReport.getFirst().setQuantity(transactionQForReport.getFirst().getQuantity() + quantityToReverse);
								}else{
									FIFOReportDTO temp = new FIFOReportDTO();
									BeanUtils.copyProperties(consumptionEntry, temp);
									temp.setQuantity(quantityToReverse);
									transactionQForReport.add(temp);
								}
							}
							reversedQuantity+=quantityToReverse;
							quantityToReverse-=quantityToReverse;
						}else{
							
							if(!consumptionEntry.getAssignmentId().equalsIgnoreCase("ApplicationGenerated")){
								if(transactionQForReport.size()>0&&transactionQForReport.getFirst().equals(consumptionEntry)){
									transactionQForReport.getFirst().setQuantity(transactionQForReport.getFirst().getQuantity() + consumptionEntry.getQuantity());
								}else{
									FIFOReportDTO temp = new FIFOReportDTO();
									BeanUtils.copyProperties(consumptionEntry, temp);
									//temp.setQuantity(quantityToReverse);
									transactionQForReport.add(temp);
								}
							}
							reversedQuantityValue+=consumptionEntry.getQuantity()*consumptionEntry.getRate();
							reversedQuantity+=consumptionEntry.getQuantity();
							quantityToReverse-=consumptionEntry.getQuantity();
							consumedStack.pop();
						}
					}
				activeRow.setClosingQuantity(activeQuantity+=reversedQuantity); 
				activeRow.setClosingValue(activeValue+=reversedQuantityValue);
				activeRow.setClosingRate((float) (activeValue/activeQuantity));
				
				activeRow.setRate((float) (reversedQuantityValue/reversedQuantity));
				activeRow.setQuantity(activeRow.getQuantity() * -1);
				activeRow.setValue(reversedQuantityValue);
				
				}*/
				else if(activeRow.getTransactionType().equalsIgnoreCase("CUSTOM") || activeRow.getTransactionType().equalsIgnoreCase("COSTPRICE") || 
						activeRow.getTransactionType().equalsIgnoreCase("RETRO") || activeRow.getTransactionType().equalsIgnoreCase("FOREX")||
						activeRow.getTransactionType().equalsIgnoreCase("INVADJ")) {
					if(activeRow.getTransactionType().equalsIgnoreCase("INVADJ")){
						if(activeRow.getQuantity()!=null)
							activeRow.setClosingQuantity(activeQuantity+activeRow.getQuantity());
						else
							activeRow.setClosingQuantity(activeQuantity);
					}
					else
						activeRow.setClosingQuantity(activeQuantity);
					
					if(activeRow.getValue()!=null)
						activeRow.setClosingValue(activeValue+=activeRow.getValue());
					else
						activeRow.setClosingValue(activeValue);//to be changed
					
					activeRow.setClosingRate((float) (activeValue / activeQuantity));
					
					//change as to pick new rate after adjustments
					consumptionRate = activeRow.getClosingRate();
					activeRow.setQuantity(null);
					activeRow.setRate(null);
	//					wacReportList.add(activeRow);
				}
				
				if(activeRow.getTransactionDate().after(reportForm.getSecStartDate()))
					finalReportList.add(activeRow);
				if(finalReportList.size() == 1 && null != previousRow ){
					finalReportList.add(finalReportList.get(0));
					previousRow.setTransactionType("OPENING");
					previousRow.setQuantity(null);
					previousRow.setRate(null);
					previousRow.setValue(null);
					previousRow.setTransactionDate(reportForm.getStartDate());
					finalReportList.set(0, previousRow);
				}
				previousRow = activeRow;
				
			}
		
			//add date in thread map
			generateExcelThread.partId_data_map.put(partIdDetails[0].toString() + "####" + partIdDetails[1].toString(), finalReportList);
//			System.out.println("size of map: " + generateExcelThread.getPartId_data_map().size());
		}
		catch (Throwable e) {
			e.printStackTrace();
		}
		finally{
			this.threadCounter.setNumberOfThreads(-1);
		}
	}

	
}
